#include <iostream>
#include <stdio.h>
#include "L1_instruction_cache.h"

/**************************
    LRU functions
***************************/

int LRU_way(LRU_2_way lru)
{
    
    return lru.bit;
}

void LRU_update(LRU_2_way& lru, unsigned int way)
{
        switch (way) {
            case 0:
                lru.bit = 1;
                break;
            case 1:    
                lru.bit = 0;
                break;
        }
}

/****************************************************
    L1_instruction_cache functions
*****************************************************/

// Searches through all the ways of the 
// corresponding index for the data by matching
// valid and tag bits, if not found returns -1
int L1_instruction_cache::searchWays(unsigned int addr)
{
    unsigned int tag = getTag(addr);
    unsigned int indx=getIndex(addr);
    unsigned int hit = -1;
    
    for (int way = 0; way<2; way++) {
        if (cache[indx][way].valid && cache[indx][way].tag == tag)
        {   
            hit = way;
            break;
        }
    }
    return hit;
}
 
//  Determines the best possible place for the replacement line. 
//  - Looks for an empty way first. If not found returns LRU.
unsigned int L1_instruction_cache::findReplacementWay(unsigned int addr){
    
    unsigned int indx = getIndex(addr);
    int emptyWay = -1;
    

    for (int way = 0; way < 2; way++)           // Loop through the 2 ways for an invalid slot 
        if (cache[indx][way].valid == false){   // if not valid     
            emptyWay = way;                     // set empty way to current way
            break;                              // break from the loop
        }
    
    if (emptyWay < 0) return LRU_way(LRU[indx]);// If all slots are occupied return LRU
    else return (unsigned int)emptyWay;         // else return the empty way
}

/***********************************************
    Read
************************************************/
unsigned int L1_instruction_cache::read(unsigned int addr)
{
    reads++;                           // increment read counter
    int hit_way = searchWays(addr);    // search the data in the cache set
    unsigned int indx = getIndex(addr);
    unsigned int offset = getOffSet(addr);
    if (hit_way != -1){                         // if hit        
        hits++;                                 // increment read counter
        // cout<<"L1 Instruction Hit.\n";                 // report hit
        LRU_update(LRU[indx], hit_way);         // Update LRU
        return cache[indx][hit_way].data[offset]; 
    }else{                                      // else 
        misses++;                               // increment miss counter
        // cout<<"L1 Instruction Miss.\n";         // report miss        
        unsigned int lru = findReplacementWay(addr);
        l2->readRequest(addr, cache[indx][lru].data);   // Read from L2 cache
        cache[indx][lru].setAddress(addr);              // Fill up the new cache line
        LRU_update(LRU[indx], lru);
        return cache[indx][lru].data[offset];
    }
}

void L1_instruction_cache::ResetState()
{
  int index;
  int way;
  for (index = 0; index < nSets; index++)
    for (way = 0; way < 2; way++)
        cache[index][way].valid = false;
}

void L1_instruction_cache::PrintContents()             //what is fprint, "a", 
{
    FILE *fod = fopen(filepath, "a");  //or "a" for append?
    if (fod == NULL) return;
    print_req++;
    fprintf(fod,"\nPrint Request: %ld\n",print_req);
    printf("\n********** Instruction Cache **********\nPrint Request: %ld\n",print_req);
    for (int indx = 0; indx <= nSets; indx++)                       //for all possible indexes, if way is valid - 
    {
        bool valid_line = false;

       for (int way = 0; way < i_nWays; way++)        //for each of these valid ways, print contents
       {
           if (cache[indx][way].valid){
               valid_line = true;
               fprintf(fod,"INDEX: 0x%-8x ",indx);
               fprintf(fod,"Way %-8d Tag: 0x%-8x  Address: 0x%-8x\n", way, cache[indx][way].tag,cache[indx][way].address);                     
               printf("INDEX: 0x%-8x ",indx);
               printf("Way %-8d Tag: 0x%-8x  Address: 0x%-8x\n", way, cache[indx][way].tag,cache[indx][way].address);                     

            }
      }
        if (valid_line){
            fprintf(fod, "LRU: %d\n",LRU_way(LRU[indx]));
            printf("LRU: %d\n",LRU_way(LRU[indx]));
        }
    }

    
    fprintf(fod,"\n");
    fflush(fod);
    fclose(fod);
}
